Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type configs #97

Merged
merged 13 commits into from
Sep 9, 2014
Merged

Type configs #97

merged 13 commits into from
Sep 9, 2014

Conversation

dannyroberts
Copy link
Member

This is a modest restructuring that makes all of a JsonObject subclass's type inferences configurable. It's done like this

class MyJsonObject(JsonObject):
    class Meta(object):
        # this replaces the default DateTimeProperty with your custom one
        # you can also completely override all properties from scratch with
        # properties = {...}
        update_properties = {datetime.datetime: MyBetterDateTimeProperty}

Now, in a MyJsonObject subclass, you'll still be able to have DateTimePropertys, but in the case of a dynamic property (one that you didn't specify in the schema) it will now default to using MyBetterDateTimeProperty.

test that the reason it was added does not break
- reverts/replaces the recent "_string_conversions" addition to the api
- adds a 'Meta' class to the JsonObject api, for configuring this kind
  of setting
- move "convert" file's parts into other files
this is to allow, for example, couchdbkit to highjack this as well
re_datetime = re.compile(
r'^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])'
'(\D?([01]\d|2[0-3])\D?([0-5]\d)\D?([0-5]\d)?\D?(\d{3,6})?'
'([zZ]|([\+-])([01]\d|2[0-3])\D?([0-5]\d)?)?)?$'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure it matters in this case, but you need to put an r in front of each line if you want each line to be that type of string literal:

>>> (r"abc\n"
...   "\ncba")
'abc\\n\ncba'

>>> (r"abc\n"
...  r"\ncba")
'abc\\n\\ncba'

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, great catch. You're right that it doesn't "matter" in this case, because two python "oddities" cancel each other out: (1) string-type prefixes only apply to the first in a multiline concatenation (ok I mean that makes sense) (2) "\d" is silently treated as two characters "\d" for any character that isn't part of an escape sequence, while "\n" is a single character. So the loosey-gooseyness of (2) obscured (1) and hasn't been causing an error.

@@ -5,7 +5,7 @@

setup(
name='jsonobject',
version='0.5.0',
version='0.6.0',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yesterday you had mentioned that you need to push this up to PyPI to test it. If you're still thinking of doing that, then what about changing the version to 0.6.0b1 or 0.6.0c1?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

# There's a pretty fundamental cyclic dependency between this metaclass
# and knowledge of all available property types (in properties module).
# The current solution is to monkey patch this metaclass
# with a reference to the properties module
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really glad to see this comment removed. Was hoping it was no longer relevant

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it was actually one very related to what I was doing, so this naturally got fixed in the process.

millerdev added a commit that referenced this pull request Sep 9, 2014
@millerdev millerdev merged commit c736667 into master Sep 9, 2014
@millerdev millerdev deleted the type-configs branch September 9, 2014 20:19

class Foo(JsonObject):
# property definitions go here
# ...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was thinking about this last night and wondering if it might make sense to put a timestamp = ... property definition here? The part below where you say "If you now do ... foo.timestamp = ..." seems pretty magic since there is no connection between foo.timestamp (indeed, it's not even defined) and MySpecialDateTimeProperty. The question I'm left with is what is making that connection? How does Foo.timestamp need to be defined to be affected by that Meta block?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha that's actually the whole point though, that you don't have to define timestamp as a property. By adding datetime.datetime: MySpecialDateTimeProperty to Meta, you're declaring that all dynamic properties that are set to a datetime should be governed by MySpecialDateTimeProperty instead of by the default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that's some pretty deep magic there. I'm guessing that MySpecialDateTimeProperty knows how to serialize a date as a string when the object is serialized as JSON, right? How does it know which strings it should deserialize when the object is loaded from persistent storage?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how this is coming out in layers. Yes, you're totally right that there needs to be some mechanism for deciding whether a dynamic string should be treated as a string or as some other type. This is done by setting string_conversions in Meta, which is basically a mapping from regular expressions to data types; if you want to see what that looks like, check the default used in the JsonObject class definition (within this PR). Keep asking questions!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thank you, string_conversions was a critical connection I had not made yet. Would it make sense to add string_conversions to the Meta class below to provide a more complete example that demonstrates all the pieces needed for a successful persist/load round-trip?

What if someone happens to put a string that successfully passes through a string conversion into a field that is supposed to remain a string?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah so that's one of those things that is carried over from couchdbkit: if you wrap JSON that has properties not listed in the schema, it automatically converts strings in particular formats to the their respective types. This PR takes a step towards loosening that hardcoded behavior, so that if you want, for example, you can set

    class Meta(object):
        string_conversions = ()

and then no dynamic string conversions happen.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One possible approach to avoid accidental reverse conversion of strings would be to store a list of the fields that were dynamically converted along with a hint about the reverse conversion procedure. Then when the object is loaded from the db that information can be used to do the reverse conversion. Maybe that's too much overhead though? (although given that it's running every string through a sequence of regexes on load...)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you're saying, but I think that that's too much of a departure from the current API to implement now without a fair amount of work in the calling code. It's also have to add some extra property like _dynamic_properties to the JSON output that the caller didn't explicitly ask for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants